package fnord.illuminati.card;

import java.util.*;

import fnord.exception.MoneyException;

public abstract class GroupCard extends Card {
	private static final long serialVersionUID = -5728535240282255006L;
	
	protected GroupCard _parent; //ALWAYS SOUTH!
	//In this map, if it has a mapping for the connection, it has that connection, and
	//if the mapping is to null, then the connection is open.
	protected Map<Connection,GroupCard> _children;
	protected int _power,_transferablepower,_resistance,_income,_money;
	protected Alignment[] _alignments;
	protected boolean _used;
	
	public GroupCard(String name, int power, int tpower, int res, int income, Alignment[] aligs) {
		_name = name;
		_power = power;
		_transferablepower = tpower;
		_resistance = res;
		_income = income;
		_money = 0;
		if (aligs != null)
			_alignments = aligs;
		else
			_alignments = new Alignment[] {};
		_used = false;
	}
	
	public GroupCard(String name, String description, int power, int tpower,
			int res, int income, Alignment[] aligs, Connection[] connections,
			Object action, ActionTime actionTime) {
		super(name, description, action, actionTime);
		_power = power;
		_transferablepower = tpower;
		_resistance = res;
		_income = income;
		_money = 0;
		if (aligs != null)
			_alignments = aligs;
		else
			_alignments = new Alignment[] {};
		if (connections != null) {
			_children = new HashMap<Connection,GroupCard>(connections.length);
			for (Connection c:connections)
				_children.put(c,null);
			}
		else
			_children = new HashMap<Connection,GroupCard>();
	}
	
	public boolean getUsed() { return _used; }
	public void setUsed(boolean used) { _used = used; }

	public int getPower() { return _power; }
	public int getTransferablePower() { return _transferablepower; }
	public int getResistance() { return _resistance; }
	public int getIncome() { return _income; }
	public int getMoney() { return _money; }
	public Alignment[] getAlignments() { return _alignments; }
	
	public Connection[] getOpenDirections() {
		Set<Connection> connections = _children.keySet();
		ArrayList<Connection> emptyconnections = new ArrayList<Connection>(connections.size());
		for (Connection c:connections) {
			if (_children.get(c)==null)
				emptyconnections.add(c);
		}
		Connection[] c = new Connection[emptyconnections.size()];
		for (int i = 0; i < emptyconnections.size(); i++) {
			c[i] = emptyconnections.get(i);
		}
//		return (Connection[])emptyconnections.toArray();
		return c;
	}
	
	
	
	
	
	public Set<Connection> getAllConnections() {
		return _children.keySet();
	}
	
	public boolean isConnectionOpen(Connection toCheck) {
		return _children.containsKey(toCheck) && _children.get(toCheck) == null;
	}
	
	public void addMoney(int m) throws MoneyException {
		if (m>=0)
			_money+=m;
		else
			throw new MoneyException("Negative money value added.");
	}
	
	public void removeMoney(int m) throws MoneyException {
		if (m<0)
			throw new MoneyException("Negative money value removed.");
		else if (_money<m)
			throw new MoneyException("Amount to remove larger than current money.");
		else
			_money-=m;
	}
	
	
	public Collection<GroupCard> getChildren() {
		ArrayList<GroupCard> children = new ArrayList<GroupCard>();
		for(GroupCard c : _children.values()) {
			if(c!=null) children.add(c);
		}
		return children;
	}
	
	public GroupCard getChild(Connection atCon) {
		return _children.get(atCon);
	}
	
	public GroupCard getParent() { return _parent; }
	
	public void setParent(GroupCard par) {
		_parent = par;
	}
	
	public boolean isAdjacent(GroupCard adjTo) {
		return adjTo.getParent()==this || this.getParent() == adjTo;
	}
	
	public void transferMoneyTo(GroupCard to, int money) throws MoneyException {
		if (money<0)
			throw new MoneyException("Negative money value removed.");
		else if (_money<money)
			throw new MoneyException("Amount to remove larger than current money.");
		else {
			to.addMoney(money);
			//if we couldn't add money, we won't get here :D, no infinite money
			_money-=money;
		}
	}
	
	public boolean transferTo(GroupCard newParent, Connection connectTo) {
		detach();
		newParent.addCard(this, connectTo);		
		return true;
	}
	
	public void addCard(GroupCard toAdd, Connection connectingBy) {
		if(_children.containsKey(connectingBy) && _children.get(connectingBy) == null) {
			_children.put(connectingBy, toAdd);
			toAdd.setParent(this);
		}
		
	}
	
	public void detach() {
		if(_parent != null) {
			_parent.removeCard(this);
			_parent = null;
		}
	}
	
	private boolean removeCard(GroupCard toRemove) {
		if(_children.containsValue(toRemove)) {
			for(Connection c : _children.keySet()) {
				if(_children.get(c) == toRemove) {
					_children.put(c, null);
					return true;
				}
			}
		}
		return false;
	}

	public Collection<GroupCard> detachAll() {
		ArrayList<GroupCard> detached = new ArrayList<GroupCard>();
		this.detach();
		detached.add(this);
		for(GroupCard c : getChildren()) {
			if(c != null) {
				detached.addAll(c.detachAll());
			}
		}
		return detached;
	}
}
